package br.ufrj.dcc.ad.event;


import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import br.ufrj.dcc.ad.args.ParametrosSimulacao;
import br.ufrj.dcc.ad.log.LoggerFactory;
import br.ufrj.dcc.ad.metrics.SystemMetrics;
import br.ufrj.dcc.ad.systems.Cliente;
import br.ufrj.dcc.ad.systems.Servidor;

public class ArriveEvent extends Event {
	private Logger debugLogger = LoggerFactory.getLogger(LoggerFactory.Type.DEBUG);
	/**
	 * Construtor usado para instanciar uma chegada na primeira fila.
	 * @param servidor O servidor para o qual o cliente deve ser encaminhado após sua espera na fila.
	 * @param tempoOcorrencia O tempo de ocorrência da chegada.
	 */
	public ArriveEvent(Servidor servidor, double tempoOcorrencia) {
		super(new Cliente(servidor.geraAmostra(0)),servidor,tempoOcorrencia);
		debugLogger.info(gerador.getNome() + " necessitará de " + gerador.getTempoConsumoRestante() + " para finalizar o seu servico na fila " + gerador.getTipoCliente());
	}
	/**
	 * Construtor usado para instanciar uma chegada nas outras filas.
	 * @param gerador O cliente que está entrando na fila.
	 * @param servidor O servidor para o qual o cliente deve ser encaminhado após sua espera na fila.
	 * @param tempoOcorrencia O tempo de ocorrência da chegada.
	 */
	public ArriveEvent(Cliente gerador, Servidor servidor, double tempoOcorrencia) {
		super(gerador,servidor,tempoOcorrencia);
	}

	@Override
	public List<Event> executaAcao() {
		//Inclui o cliente gerador do evento na fila do seu tipo.
		servidor.getFilasDePrioridade().get(gerador.getTipoCliente()).add(gerador);
		//Guarda o último tempo de chegada na fila.
		if(gerador.getTemposUltimasEntradasFilas().size() > gerador.getTipoCliente()){
			gerador.getTemposUltimasEntradasFilas().set(gerador.getTipoCliente(),tempoOcorrencia);
		}else{
			gerador.getTemposUltimasEntradasFilas().add(gerador.getTipoCliente(),tempoOcorrencia);
		}
		
		List<Event> eventosInvocacao = new ArrayList<Event>();
		
		//Se o cliente que está chegando é mais prioritário que o atual cliente no servidor...
		if((servidor.getConsumidor() != null) && (gerador.comparePrioridade(servidor.getConsumidor()) > 0)) {
			//...retire o cliente recem chegado de sua fila.
			servidor.getFilasDePrioridade().get(gerador.getTipoCliente()).remove(gerador);
			//... e gere interrupção imediata.
			eventosInvocacao.add(new InterruptEvent(gerador, servidor, tempoOcorrencia));
		}
		
		//Se o servidor está vazio.
		if(!servidor.isOcupado() ) {
			Cliente proximoCliente = servidor.getAndRemoveProximoCliente();
			if(proximoCliente != null){
				//O evento de saida de uma fila é um evento de entrada no servidor.
				ServerAtendmentEvent serverAtendmentEvent = 
						new ServerAtendmentEvent(proximoCliente, servidor, tempoOcorrencia);
				eventosInvocacao.add(serverAtendmentEvent);
				//Pode ser que existam outros eventos de consumo agendado 
				//para clientes com menor prioridade que este, estes devem ser cancelados.
				eventosInvocacao.add(new CancelScheduledAtendmentsEvent(proximoCliente,serverAtendmentEvent,servidor,tempoOcorrencia));
			}
		}
		//Descubra qual será a próxima chegada externa caso estivermos tratando uma chegada externa.
		if(gerador.getTipoCliente() == 0){
			try{
				//Atualiza número de clientes no sistema:
				SystemMetrics.setNumeroClientesSistema(SystemMetrics.getNumeroClientesSistema() + 1);
				//Gera o evento da próxima chegada na primeira fila.
				eventosInvocacao.add(new ArriveEvent(servidor, ParametrosSimulacao.geraAmostraChegada(tempoOcorrencia)));
			}catch(Exception e){
				e.printStackTrace();
			}
			
		}
		return eventosInvocacao;
	}
}
